Previous preliminary work (Amante, Kilcher, Roberts, & Draxl, 2016) and a couple industry reports (Communications Security, Reliability and Interoperability Council IV, 2014, 2016) inform this report.
And another reference (López, Andreu, Ceballos, Martínez de Alegría, & Kortabarria, 2013).
Marine renewable energy is an important contribution to reducing our dependency on fossil fuels and carbon footprint contributing to climate change.
“An effective submarine cable protection regime must account for the physical characteristics of submarine cables and the mechanical characteristics of the installation vessels and tools. These characteristics—along with weather, sea, and seabed conditions—greatly determine the specifics of a given cable installation or repair.” (Communications Security, Reliability and Interoperability Council IV, 2014)
Overlap of cables an issue? Yes
“Clustering of Submarine Cable Systems: The clustering of submarine cables along particular routes (whether to avoid unfavorable sea floor topography, natural hazards, or man-made hazards such as dredging and dumping areas, fishing grounds, and energy infrastructure) increases the risk that installation or maintenance of one cable will cause direct physical disturbance to another, such as with plowing and grappling operations.” (Communications Security, Reliability and Interoperability Council IV, 2014)
Are cost savings possible through sharing cable infrastructure & maintenance between marine renewable energy transmission and existing submarine cables?
Direct ecological benefits can result from developing offshore energy, such as de facto reserve creation and creation of hard surfaces for habitat forming species. Are other benefits possible with coordinate marine spatial planning, particularly between cable and renewable industries?
(A. B. Gill, 2005; Inger et al., 2009; Lester et al., 2013; Pelc & Fujita, 2002; Willsteed, Gill, Birchenough, & Jude, 2017)
US EEZ for Pacific and Atlantic. Alaska & Hawaii?
See Table 2.1.
library(tidyverse)
read_csv('../data/data_sources.csv') %>%
kable(
caption = 'Data sources from preliminary report.', booktabs=T)
| Data | Source | Website |
|---|---|---|
| Offshore Cables | National Oceanic and Atmospheric Administration (NOAA) | http://marinecadastre.gov/data/ |
| Bathymetry | General Bathymetric Chart of the Oceans (GEBCO) | http://www.gebco.net/data_and_products/gridded_bathymetry_data/ |
| U.S. Shoreline | NOAA | http://shoreline.noaa.gov/data/datasheets/medres.html |
| Tide | Georgia Tech Research Corporation | http://www1.eere.energy.gov/water/pdfs/1023527.pdf |
| Wave | Electric Power Research Institute | http://www1.eere.energy.gov/water/pdfs/mappingandassessment.pdf |
| Wind (100m height) | AWS Truepower, LLC for windNavigator | http://apps2.eere.energy.gov/wind/windexchange/windmaps/ |
See Figure 2.1.
library(rgdal)
library(rgeos)
library(leaflet)
#setwd('~/github/nrel-cables')
gdb = '../data/SubmarineCables/NOAAChartedSubmarineCables.gdb'
fc = ogrListLayers(gdb)[[1]]
lns = readOGR(dsn=gdb, layer=fc, verbose=F)
# TODO: move these danglers to Pacific left of existing
idx = gCentroid(lns, byid=T) %>% coordinates() %>% .[,1] < 100
lns = subset(lns, idx)
leaflet(lns) %>%
addProviderTiles('Esri.OceanBasemap') %>% # 'Esri.OceanBasemap'/'Stamen.TonerLite'
# see [all providers](http://leaflet-extras.github.io/leaflet-providers/preview/index.html)
addPolylines(
popup = ~sprintf("scaleBand: %s<br>description: %s<br>effectiveDate: %s", scaleBand, description, effectiveDate))
Figure 2.1: Map of NOAA Charted Submarine cables in the United States as of December 2012.
The bathymetric depth comes from the GEBCO 30 arc-second grid. Here’s there requested attribution:
GEBCO_2014 Grid, version 20150318, www.gebco.net
NREL.
“ICPC Recommendation 2 No. 10: parallel submarine cables maintain a separation distance of the lesser of 3 times depth of water or (where not achievable) 2 times the depth of water following consultation and agreement between affected parties — a separation standard the principles of which also apply to spacing of submarine cables and other marine infrastructure”
At least 2 separation zones based on depth:
Minimum: 2 times the depth of water
Recommended: 3 times depth of water
Psudocode:
# convert depth (GEBCO 30 sec resolution) to polygons of cells
depth_cells = as.polygon(depth_raster)
# intersect cables with depth cells
cable_cells = intersect(cables_lines, depth_cells)
# buffer based on depth
cables_buffers = list()
for (x in unique(cable_cells$depth)){
cables_buffers[str(x)] = cable_cells %>%
subset(depth == x) %>%
buffer(x)
}
cable_buf = merge(cables_buffers)
# smooth out jagged edges between cells
cable_buf = simplify(cable_buf)
Initially, let’s try the depth-varying buffer on a small experimental area of Morro Bay, CA where a floating offshore wind farm is currently proposed amongst California Activities | BOEM. We’ll scale up to national later.
Here’s the GEBCO depth for our initial Morro Bay study area:
library(raster)
select = dplyr::select
#library(marmap)
# files
depth_nc = '../data/big/GEBCO_2014_2D.nc' # 1.87 GB -- too big for Github
# load depth
depth_r = raster(depth_nc, layer = 'elevation')
# extract to extent of submarine cable lines
#depth_rc = crop(depth_r, extent(lns))
xmin = -121.29; xmax = -120.59; ymin = 35.04; ymax = 35.55
depth_rc = crop(depth_r, extent(xmin, xmax, ymin, ymax)) # Morro Bay test
depth_rc[depth_rc > 0] = NA
depth_rc = depth_rc * -1
#plot(depth_rc)
Let’s plot this in an interactive map interface:
pal <- colorNumeric(
c("blue","purple"), # RColorBrewer::display.brewer.all()
values(depth_rc), na.color = "transparent")
leaflet() %>%
addProviderTiles("Stamen.TonerLite") %>%
addRasterImage(depth_rc, colors = pal, opacity = 0.8) %>%
addLegend(pal = pal, values = values(depth_rc),
title = "Depth") %>%
addPolylines(
data = lns, color='orange',
popup = ~sprintf(
"scaleBand: %s<br>description: %s<br>effectiveDate: %s",
scaleBand, description, effectiveDate)) %>%
setView(
seq(xmin, xmax, length.out=3)[2],
seq(ymin, ymax, length.out=3)[2],
zoom = 10)
Now we’ll apply a depth-varying buffer to the offshore cables, 2 * depth for “minimum” and 3 * depth for “recommended” separation zones for routing new cables, by first intersecting depth with cables, then iterating over each depth to apply the appropriate buffers before finally dissolving all buffers. In order to apply the buffer, I needed to project from geographic coordinates to a projection that minimizes area distortion, so chose Albers Equal Area and applied the “one-sixth rule” based on the extent of the cable features to minimize distortion.
library(rgeos)
library(geosphere)
library(sf)
rds_dx2 = '../data/morro-bay_2xdepth.rds'
rds_dx3 = '../data/morro-bay_3xdepth.rds'
if(any(!file.exists(rds_dx2), !file.exists(rds_dx2))){
# convert raster to vector
depth_p = rasterToPolygons(depth_rc, dissolve=T)
names(depth_p@data) = 'depth'
#plot(depth_p)
lns_t = spTransform(lns, crs(depth_p))
lns_i = raster::intersect(lns_t, depth_p) # 3 secs
depths_m = lns_i@data %>%
group_by(depth) %>%
summarize(n = n())
# depths_m %>%
# arrange(desc(n))
# Albers Equal Area, with one-sixth rule
bb = bbox(lns_i)
lat_range = extendrange(bb['y',],f=-1/6)
crs_aea = CRS(
sprintf(
"+proj=aea +lat_1=%f +lat_2=%f +lat_0=%f +lon_0=%f +x_0=0 +y_0=0 +datum=WGS84 +ellps=WGS84 +units=m +no_defs",
lat_range[1], lat_range[2], mean(lat_range), mean(bb['x',])))
lns_a = spTransform(lns_i, crs_aea)
#for (d in depths_m$depth){ # m = 8
for (i in 1:nrow(depths_m)){ # i = 2
d = depths_m$depth[i]
#cat(sprintf('%03d of %d: %d m\n', i, nrow(depths_m), d))
lns_d = subset(lns_a, depth==d)
buf_dx2 = gBuffer(lns_d, width=d*2)
buf_dx3 = gBuffer(lns_d, width=d*3)
if (i == 1){
ply_dx2 = buf_dx2
ply_dx3 = buf_dx3
} else {
ply_dx2 = raster::union(ply_dx2, buf_dx2)
ply_dx3 = raster::union(ply_dx3, buf_dx3)
}
}
ply_dx2 = gUnaryUnion(ply_dx2)
ply_dx3 = gUnaryUnion(ply_dx3)
ply_dx2 = spTransform(ply_dx2, crs(depth_p))
ply_dx3 = spTransform(ply_dx3, crs(depth_p))
saveRDS(ply_dx2, rds_dx2)
saveRDS(ply_dx3, rds_dx3)
}
ply_dx2 = readRDS(rds_dx2)
ply_dx3 = readRDS(rds_dx3)
leaflet() %>%
addProviderTiles("Stamen.TonerLite", group = "B&W") %>%
addProviderTiles('Esri.OceanBasemap', group = "Ocean") %>% #
addRasterImage(
depth_rc, group='depth',
colors = pal, opacity = 0.8) %>%
addLegend(
pal = pal, values = values(depth_rc),
title = "Depth", position='bottomright') %>%
addPolygons(
data = ply_dx3, group='buffer: 3*depth',
color='red', stroke=F, fillOpacity = 0.5) %>%
addPolygons(
data = ply_dx2, group='buffer: 2*depth',
color='red', stroke=F, fillOpacity = 0.5) %>%
addPolylines(
data = lns, group='cables',
color='black', opacity = 1, weight=1,
popup = ~sprintf(
"scaleBand: %s<br>description: %s<br>effectiveDate: %s",
scaleBand, description, effectiveDate)) %>%
addLayersControl(
baseGroups = c("B&W", "Ocean"),
overlayGroups = c('depth', 'buffer: 3*depth', 'buffer: 2*depth', 'cables'),
options = layersControlOptions(collapsed = FALSE)) %>%
setView(
seq(xmin, xmax, length.out=3)[2],
seq(ymin, ymax, length.out=3)[2],
zoom = 10)
Next steps. The above R code works well for this small area, but will not scale well to the national level. I’ll implement the intersection, buffering and dissolve operations in a PostGIS spatial database, which has the advantage of much more efficient spatial indexing and operations.
“ICPC Recommendation 13 No. 2, which establishes a methodology for determining site-specific proximity limits between submarine cables and offshore wind facilities and a default separation distance in shallower waters of 500 meters on either side of an in-service submarine cable — a separation standard the principles of which also apply to other offshore renewable energy projects.”
“Subsea Cables UK Guideline No. 6 (endorsed by NASCA), which establishes principles for determining safe proximity distances and negotiating proximity agreements between offshore wind farms and submarine cables and reflects extensive experience in the United Kingdom with managing spatial conflicts between offshore wind farms and submarine cables.”
“endorse a default separation distance of 500 meters in water depths of less than 75 meters and the greater of 500 meters or two times the depth of water in greater water depths.”
<= 250 m: 500 m> 250 m: 2 x depthPsudocode:
# buffer based on depth
cables_buffers = list()
for (x in unique(cable_cells$depth)){
cables_buffers[str(x)] = cable_cells %>%
subset(depth == x) %>%
buffer(x)
}
cable_buf = merge(cables_buffers)
# smooth out jagged edges between cells
cable_buf = simplify(cable_buf)
dx2_rds = sprintf('../data/ca-coast_2xdepth-classes.rds')
dx3_rds = sprintf('../data/ca-coast_3xdepth-classes.rds')
ply_dx2 = readRDS(dx2_rds)
ply_dx3 = readRDS(dx3_rds)
pal <- colorNumeric(
c("blue","purple"), # RColorBrewer::display.brewer.all()
values(depth_rc), na.color = "transparent")
m = leaflet() %>%
addProviderTiles("Stamen.TonerLite", group = "B&W") %>%
addProviderTiles('Esri.OceanBasemap', group = "Ocean") %>% #
addRasterImage(
depth_rc, group='depth',
colors = pal, opacity = 0.8) %>%
addLegend(
pal = pal, values = values(depth_rc),
title = "Depth", position='bottomright') %>%
addPolygons(
data = ply_dx3, group='buffer: 3*depth',
color='red', stroke=F, fillOpacity = 0.5) %>%
addPolygons(
data = ply_dx2, group='buffer: 2*depth',
color='red', stroke=F, fillOpacity = 0.5) %>%
addPolylines(
data = lns, group='cables',
color='black', opacity = 1, weight=1,
popup = ~sprintf(
"scaleBand: %s<br>description: %s<br>effectiveDate: %s",
scaleBand, description, effectiveDate)) %>%
addLayersControl(
baseGroups = c("B&W", "Ocean"),
overlayGroups = c('depth', 'buffer: 3*depth', 'buffer: 2*depth', 'cables'),
options = layersControlOptions(collapsed = FALSE))
bb = extent(depth_rc)
m %>%
fitBounds(bb@xmin, bb@ymin, bb@xmax, bb@ymax)
In order to apply the depth-varying buffer over the entirety of the cable layer without hitting memory issues, I divided the area into 32 tiles. The first tile took about 80 minutes [* 32 tiles / 60 min per hr = 42.67 hrs for all].
library(rgeos)
library(geosphere)
library(sf)
library(tidyverse)
d_incr = 100 # depth increment
dx2_rds = sprintf('../data/buf_2xdepth-incr%sm.rds', d_incr)
dx3_rds = sprintf('../data/buf_3xdepth-incr%sm.rds', d_incr)
if(any(!file.exists(dc2_rds), !file.exists(dc3_rds))){
# setup tiles for iteration
bb = extent(lns)
rx = c(floor(bb@xmin), ceiling(bb@xmax))
ry = c(floor(bb@ymin), ceiling(bb@ymax))
dx = diff(rx)
dy = diff(ry)
nx = 8
ny = 4
tx = dx/nx
ty = dy/ny
xseq = seq(rx[1], by=tx, length.out=nx)
yseq = seq(ry[1], by=ty, length.out=ny)
xyseq = data_frame(xmin=numeric(0), xmax=numeric(0), ymin=numeric(0), ymax=numeric(0))
for (x in xseq){
for (y in yseq){
xyseq = bind_rows(
xyseq,
data_frame(
xmin=x, xmax=x+tx, ymin=y, ymax=y+ty)
)
}
}
# iterate over tiles
for (i in 1:nrow(xyseq)){ # i = 1
# setup extent
e_i = with(xyseq[i,], extent(xmin, xmax, ymin, ymax))
cat(sprintf('%02d: %0.1f,%0.1f -- %s\n', i, e_i@xmin, e_i@ymin, Sys.time()))
# crop lines
lns_i = crop(lns, e_i)
# skip tile if not lines
if (length(lns_i)) next
# clip depth to tile
depth_i = crop(depth_r, e_i)
depth_i[depth_i > 0] = NA
depth_i = depth_i * -1
# create depth reclass table
tbl_reclass = data_frame(
depth_from=0, depth_to=250, depth_1x=250) %>%
bind_rows(
data_frame(
depth_1x = seq(250 + d_incr/2, cellStats(depth_i, 'max'), by=d_incr)) %>%
mutate(
depth_from = depth_1x - d_incr/2,
depth_to = depth_1x + d_incr/2)) %>%
mutate(
depth_2x = depth_1x*2,
depth_3x = depth_1x*3)
#tbl_reclass %>% DT::datatable() # n=51
# reclassify raster
cat(sprintf(' reclassify raster & convert to vector -- %s\n', Sys.time()))
r_d1x = reclassify(depth_i, tbl_reclass %>% select(depth_from, depth_to, depth_1x)) # plot(r_d1x)
# convert raster to vector
system.time({
p_d1x = rasterToPolygons(r_d1x, dissolve=T) # tiles:nx = 8;ny = 4 -> 79.2 min [ * 32 rows / 60 min per hr = 42.2 hrs ]
})
names(p_d1x@data) = 'buf1x'
# project & intersect lns to depth vector
lns_t = spTransform(lns, crs(p_d1x))
system.time({
lns_i = raster::intersect(lns_t, p_d1x) # tiles:nx = 8;ny = 4 -> 1.7 min [ * 32 rows / 60 min per hr = 53.3 min ]
})
# project to Albers Equal Area (with one-sixth rule) for units of meters
bb = bbox(lns_i)
lat_range = extendrange(bb['y',],f=-1/6)
crs_aea = CRS(
sprintf(
"+proj=aea +lat_1=%f +lat_2=%f +lat_0=%f +lon_0=%f +x_0=0 +y_0=0 +datum=WGS84 +ellps=WGS84 +units=m +no_defs",
lat_range[1], lat_range[2], mean(lat_range), mean(bb['x',])))
lns_a = spTransform(lns_i, crs_aea) # plot(lns_a)
# iterate over buffer depths
bufs = lns_i@data %>%
group_by(buf1x) %>%
summarize(n = n())
for (j in 1:nrow(bufs)){ # j = 1
b = bufs$buf1x[j]
cat(sprintf(' %03d of %d bufs: %d m -- %s\n', j, nrow(bufs), b, Sys.time()))
lns_b = subset(lns_a, buf1x==b)
buf_dx2 = gBuffer(lns_b, width=b*2)
buf_dx3 = gBuffer(lns_b, width=b*3)
if (j == 1){
ply_dx2_i = buf_dx2
ply_dx3_i = buf_dx3
} else {
ply_dx2_i = raster::union(ply_dx2_i, buf_dx2)
ply_dx3_i = raster::union(ply_dx3_i, buf_dx3)
}
} # 3 min
ply_dx2_i = gUnaryUnion(ply_dx2_i)
ply_dx3_i = gUnaryUnion(ply_dx3_i)
} # finish iterating over tiles
# merge tiles
if (i == 1){
ply_dx2 = ply_dx2_i
ply_dx3 = ply_dx3_i
} else {
ply_dx2 = raster::union(ply_dx2, ply_dx2_i)
ply_dx3 = raster::union(ply_dx3, ply_dx3_i)
}
# project back to lon/lat
ply_dx2 = spTransform(ply_dx2, crs(p_d1x))
ply_dx3 = spTransform(ply_dx3, crs(p_d1x))
# save to filesystem
saveRDS(ply_dx2, dx2_rds)
saveRDS(ply_dx3, dx3_rds)
}
Extract average and area of overlap between zones and renewable energy potential areas, possibly at various depth bins.
units: wind speed (m/s) at 90m hub height
library(tidyverse)
library(sf)
library(raster)
e_ca = extent(-117,-125,32,39)
shp = '/Volumes/Best HD/nrel_data_big/nrel.gov/wind/pac/pacific_coast_90mwindspeed_off.shp'
ply_ca = read_sf(shp) %>% as('Spatial') %>%
crop(e_ca)
## Reading layer `pacific_coast_90mwindspeed_off' from data source `/Volumes/Best HD/nrel_data_big/nrel.gov/wind/pac/pacific_coast_90mwindspeed_off.shp' using driver `ESRI Shapefile'
## Simple feature collection with 11028 features and 3 fields
## geometry type: POLYGON
## dimension: XY
## bbox: xmin: -126.018 ymin: 31.96705 xmax: -117.097 ymax: 49.00289
## epsg (SRID): 4326
## proj4string: +proj=longlat +datum=WGS84 +no_defs
plot(ply_ca['Speed_90'])
units: wave energy flux (kW/m)
shp = '/Volumes/Best HD/nrel_data_big/nrel.gov/wave/mhk-atlas_wave_wef_ann/wave_wef_ann.shp'
ply_ca = read_sf(shp) %>% as('Spatial') %>%
crop(e_ca)
## Reading layer `wave_wef_ann' from data source `/Volumes/Best HD/nrel_data_big/nrel.gov/wave/mhk-atlas_wave_wef_ann/wave_wef_ann.shp' using driver `ESRI Shapefile'
## Simple feature collection with 42234 features and 16 fields
## geometry type: POLYGON
## dimension: XY
## bbox: xmin: -180 ymin: 16.8333 xmax: 179.9978 ymax: 63.0206
## epsg (SRID): 4326
## proj4string: +proj=longlat +datum=WGS84 +no_defs
plot(ply_ca['ann_wef'])
units: mean power
shp = '/Volumes/Best HD/nrel_data_big/nrel.gov/tide/tide_data_west/tide_data_west.shp'
ply_ca = read_sf(shp) %>% as('Spatial') %>%
crop(e_ca)
## Reading layer `tide_data_west' from data source `/Volumes/Best HD/nrel_data_big/nrel.gov/tide/tide_data_west/tide_data_west.shp' using driver `ESRI Shapefile'
## Simple feature collection with 1595806 features and 69 fields
## geometry type: POINT
## dimension: XY
## bbox: xmin: -179.6531 ymin: 32.39362 xmax: -117.0972 ymax: 61.48098
## epsg (SRID): 4269
## proj4string: +proj=longlat +datum=NAD83 +no_defs
plot(ply_ca['MEANPO'])
Products will be online and readily digestable by stakeholders.
Amante, C., Kilcher, L., Roberts, B., & Draxl, C. (2016). Offshore Cable Analysis: Pilot Study.
Communications Security, Reliability and Interoperability Council IV. (2014). Protection of Submarine Cables Through Spatial Separation.
Communications Security, Reliability and Interoperability Council IV. (2016). Clustering of Cables and Cable Landings.
Gill, A. B. (2005). Offshore renewable energy: Ecological implications of generating electricity in the coastal zone. Journal of Applied Ecology, 42(4), 605–615. https://doi.org/10.1111/j.1365-2664.2005.01060.x
Inger, R., Attrill, M. J., Bearhop, S., Broderick, A. C., James Grecian, W., Hodgson, D. J., … Godley, B. J. (2009). Marine renewable energy: Potential benefits to biodiversity? An urgent call for research. Journal of Applied Ecology, 46(6), 1145–1153. https://doi.org/10.1111/j.1365-2664.2009.01697.x
Lester, S. E., Costello, C., Halpern, B. S., Gaines, S. D., White, C., & Barth, J. A. (2013). Evaluating tradeoffs among ecosystem services to inform marine spatial planning. Marine Policy, 38, 80–89. https://doi.org/10.1016/j.marpol.2012.05.022
López, I., Andreu, J., Ceballos, S., Martínez de Alegría, I., & Kortabarria, I. (2013). Review of wave energy technologies and the necessary power-equipment. Renewable and Sustainable Energy Reviews, 27, 413–434. https://doi.org/10.1016/j.rser.2013.07.009
Pelc, R., & Fujita, R. M. (2002). Renewable energy from the ocean. Marine Policy, 26(6), 471–479. https://doi.org/10.1016/S0308-597X(02)00045-3
Willsteed, E., Gill, A. B., Birchenough, S. N. R., & Jude, S. (2017). Assessing the cumulative environmental effects of marine renewable energy developments: Establishing common ground. Science of The Total Environment, 577, 19–32. https://doi.org/10.1016/j.scitotenv.2016.10.152